package org.mengyun.tcctransaction.spring.support;

import org.mengyun.tcctransaction.support.BeanFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by changmingxie on 11/22/15.
 */
@Component
public class TccApplicationContext implements BeanFactory, ApplicationContextAware {
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public Object getBean(Class<?> aClass) {
        return this.applicationContext.getBean(aClass);
    }
    @Override
    public Object getBean(String name) {
        return this.applicationContext.getBean(name);
    }
    @Override
    public Object getBean(String name, Class<?> aClass) {
        return this.applicationContext.getBean(name, aClass);
    }

    @Override
    public String getBeanName(Object bean, Class targetClass) {
        Object beanTargetInDubbo = getBeanTargetInDubbo(bean);

        try {
            Map<Object, String> beans = getBeansOfTypeCached(targetClass);
            if (null == beans)
                return null;

            return beans.get(beanTargetInDubbo);
        } catch (Exception e) {
            return null;
        }
    }


    private final Map<Class, Map<Object, String>> classBeansCacheMap = new HashMap<>();
    private Map<Object, String> getBeansOfTypeCached(Class targetClass) {
        Map<Object, String> result = classBeansCacheMap.get(targetClass);
        if (null != result)
            return result;


        Map<String, Object> beansOfThisType = applicationContext.getBeansOfType(targetClass);
        if (null == beansOfThisType)
            return null;

        result = new HashMap<>();
        for (Map.Entry<String, Object> entry : beansOfThisType.entrySet()) {
            result.put(getBeanTargetInDubbo(entry.getValue()), entry.getKey());
        }


        classBeansCacheMap.put(targetClass, result);

        return result;
    }

    // 获取dubbo底层的bean
    private static Object getBeanTargetInDubbo(Object bean) {
        try {
            Field callback1Field = bean.getClass().getDeclaredField("CGLIB$CALLBACK_1");
            if (null == callback1Field)
                return bean;
            callback1Field.setAccessible(true);
            Object callback1 = callback1Field.get(bean);
            if (null == callback1)
                return bean;

            Field targetField = callback1.getClass().getDeclaredField("target");
            if (null == targetField)
                return bean;
            targetField.setAccessible(true);
            Object target = targetField.get(callback1);
            if (null != target)
                return target;

            return bean;
        } catch (Exception e) {
            return bean;
        }
    }
}
